#include "StdAfx.h"
#include "ResourceMng.h"

CResourceMng::CResourceMng(void)
{
}

CResourceMng::~CResourceMng(void)
{
}

int infs2( unsigned char *in, int size, int realSize, unsigned char *&out, int wBits = 15 )
{
	int ret;
	unsigned have;
	z_stream strm;
	int latPosOut = 0;

	/* allocate inflate state */
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;
	strm.avail_in = 0;
	strm.next_in = Z_NULL;
	ret = inflateInit2(&strm, wBits);
	if (ret != Z_OK)
		return ret;
	int chunkNumber = 0;
	do
	{
		if(size < chunkNumber * 50000)
			break;
		strm.avail_in = std::min(50000, size - chunkNumber * 50000);
		if (strm.avail_in == 0)
			break;
		strm.next_in = in + chunkNumber * 50000;

		/* run inflate() on input until output buffer not full */
		do
		{
			strm.avail_out = realSize - latPosOut;
			strm.next_out = out + latPosOut;
			ret = inflate(&strm, Z_NO_FLUSH);
			//assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
			bool breakLoop = false;
			switch (ret)
			{
			case Z_STREAM_END:
				breakLoop = true;
				break;
			case Z_NEED_DICT:
				ret = Z_DATA_ERROR;	 /* and fall through */
			case Z_DATA_ERROR:
			case Z_MEM_ERROR:
				(void)inflateEnd(&strm);
				return ret;
			}

			if(breakLoop)
				break;

			have = realSize - latPosOut - strm.avail_out;
			latPosOut += have;
		} while (strm.avail_out == 0);

		++chunkNumber;
		/* done when inflate() says it's done */
	} while (ret != Z_STREAM_END);

	/* clean up and return */
	(void)inflateEnd(&strm);
	return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

int CResourceMng::InitH3Resource( xgc_lpcstr path )
{
	FILE *fp = fopen( path, "rb" );
	if( fp == xgc_nullptr )
		return -1;

	unsigned int total = 0;
	fseek( fp, 8, SEEK_SET );
	fread( &total, 4, 1, fp );

	fseek( fp, 0x5c, SEEK_SET );

	unsigned char* entrys = ( unsigned char* )malloc( sizeof(LodEntry)*total );
	if( entrys )
	{
		size_t ret = fread( entrys, sizeof(LodEntry), total, fp );
		LodEntry* pEntry = (LodEntry*)entrys;

		xgc_byte* buffer = (xgc_byte*)malloc(1024*64);
		xgc_byte* uncompressed = (xgc_byte*)malloc(1024*128);

		for( unsigned int i = 0, j = 0; i < total; ++i, ++j )
		{
			printf( "%s : offset %d, uncompressedSize %d, size %d\n", pEntry[i].filename, pEntry[i].offset, pEntry[i].uncompressedSize, pEntry[i].size );

			if( pEntry[i].size >= _msize(buffer) )
			{
				buffer = (xgc_byte*)realloc(buffer, XGC_ALIGNED_SIZE(pEntry[i].size, 1024*64));
				if( buffer == xgc_nullptr )
					break;
			}

			if( pEntry[i].uncompressedSize >= _msize(uncompressed) )
			{
				uncompressed = (xgc_byte*)realloc(uncompressed, XGC_ALIGNED_SIZE(pEntry[i].uncompressedSize, 1024*64));
				if( uncompressed == xgc_nullptr )
					break;
			}

			fseek( fp, pEntry[i].offset, SEEK_SET );

			if( pEntry[i].size == 0 ) //no compress
			{
				fread( buffer, pEntry[i].uncompressedSize, 1, fp );
				memcpy( uncompressed, buffer, pEntry[i].uncompressedSize );
			}
			else // uncompress
			{
				fread( buffer, pEntry[i].size, 1, fp );
				if( Z_OK != infs2( buffer, pEntry[i].size, pEntry[i].uncompressedSize, uncompressed ) )
					printf( "uncompress failed!" );
			}

			char* ext = strrchr(pEntry[i].filename, '.');
			if( ext ) ++ext;

			if( stricmp( ext, "txt" ) == 0 )
			{
				CreateText( pEntry[i], uncompressed );
			}
			else if( stricmp( ext, "pcx" ) == 0 )
			{
				CreateSprite( pEntry[i], uncompressed );
			}
			else if( stricmp( ext, "def" ) == 0 )
			{
				CreateAnimation( pEntry[i], uncompressed );
			}
		}

		free( buffer );
		free( uncompressed );
		free( entrys );
	}

	fclose( fp );
	return 0;
}

hgeSprite* CResourceMng::CreateSprite( const LodEntry& entry, xgc_byte* uncompressed )
{
	SpriteMap::iterator i = smap.find( entry.filename );
	if( i != smap.end() )
	{
		return &i->second;
	}

	HGE* hge = hgeCreate( HGE_VERSION );

	unsigned int* header = (unsigned int*)uncompressed;
	unsigned int image	= header[0];
	unsigned int width	= header[1];
	unsigned int height	= header[2];
	unsigned int mask	= image / (width*height);

	HTEXTURE texture = hge->Texture_Create( width, height );

	switch( mask )
	{
		case 1:
			{
				xgc_byte* palette	= uncompressed + image + 0x0c;
				xgc_byte* image		= uncompressed + 0xc;
				unsigned int *texp	= (unsigned int*)hge->Texture_Lock( texture, false, 0, 0, width, height );
				for( xgc_uint32 h = 0; h < height; ++h )
				{
					for( xgc_uint32 w = 0; w < width; ++w )
					{
						xgc_byte* color = palette + image[w]*3;
						texp[w] = ARGB(255,color[0],color[1],color[2]);
					}

					texp	+= width;
					image	+= width;
				}
				hge->Texture_Unlock( texture );
			}
			break;
		case 2:
			{
				xgc_byte* image		= uncompressed + 0xc;
				unsigned int *texp	= (unsigned int*)hge->Texture_Lock( texture, false, 0, 0, width, height );
				for( xgc_uint32 h = 0; h < height; ++h )
				{
					for( xgc_uint32 w = 0; w < width; ++w )
					{
						xgc_byte* color = image + w*4;
						texp[w] = ARGB(255,color[0],color[1],color[2]);
					}

					texp	+= width;
					image	+= width;
				}
				hge->Texture_Unlock( texture );
			}
			break;
	}
	hge->Release();

	SpriteMap::_Pairib ib = smap.insert( std::make_pair(entry.filename, hgeSprite( texture, 0.0f, 0.0f, width*1.0f, height*1.0f )) );

	return ib.second?&(ib.first->second):xgc_nullptr;
}

struct frame
{
	const xgc_byte*	image;
	xgc_uint32	size;
	xgc_uint32	format;
	xgc_uint32	full_W;
	xgc_uint32	full_H;
	xgc_uint32	width;
	xgc_uint32	height;
	xgc_uint32	left_margin;
	xgc_uint32	top_margin;

	bool operator==( const frame& rhs )const { return image == rhs.image; }
};

hgeAnimation* CResourceMng::CreateAnimation( const LodEntry& entry, xgc_byte* uncompressed )
{
	static xgc_uint32 G_Palette[8] =
	{
		ARGB(  0, 0, 0, 0),// 100% - transparency
		ARGB(192, 0, 0, 0),//  75% - shadow border,
		ARGB(128, 0, 0, 0),// TODO: find exact value
		ARGB(128, 0, 0, 0),// TODO: for transparency
		ARGB(128, 0, 0, 0),//  50% - shadow body
		ARGB(  0, 0, 0, 0),// 100% - selection highlight
		ARGB(128, 0, 0, 0),//  50% - shadow body   below selection
		ARGB(192, 0, 0, 0), // 75% - shadow border below selection
	};

	HGE* hge = hgeCreate( HGE_VERSION );

	const xgc_byte* read_cursor = uncompressed;
	// bufstream s( uncompressed, entry.uncompressedSize );
	xgc_uint32 type		= reader< xgc_uint32 >( read_cursor, &read_cursor );
	xgc_uint32 _width	= reader< xgc_int32 >( read_cursor, &read_cursor );
	xgc_uint32 _height	= reader< xgc_int32 >( read_cursor, &read_cursor );

	xgc_uint32 blocks	= reader< xgc_uint32 >( read_cursor, &read_cursor );

	xgc_uint32 palette[256];

	for( xgc_uint32 i= 0; i < 256; i++ )
	{
		const xgc_byte* color = read_cursor;
		palette[i] = ARGB( 255, color[0], color[1], color[2] );

		read_cursor += 3;
	}

	if (type == 71)//Buttons/buildings don't have shadows\semi-transparency
		memset(palette, 0, sizeof(xgc_uint32)*_countof(G_Palette));
	else
		memcpy(palette, G_Palette, sizeof(xgc_uint32)*_countof(G_Palette));//initialize shadow\selection colors

	std::pair< HTEXTURE, AniBlockMap >& Animation = amap[entry.filename];

	AniBlockMap &groups = Animation.second;

	std::vector< frame > sprites;

	xgc_uint32 width_max = 0, height_max = 0;

	for( xgc_uint32 i = 0; i < blocks; ++i )
	{
		xgc_uint32 block	= reader< xgc_int32 >( read_cursor, &read_cursor );
		xgc_uint32 entrys	= reader< xgc_int32 >( read_cursor, &read_cursor );

		//8 unknown bytes - skipping
		read_cursor += 8;

		//13 bytes for name of every frame in this block - not used, skipping
		read_cursor += 13*entrys;

		xgc_uint32 offset;
		Sequence& index_seq = groups[block];
		for( xgc_uint32 j = 0; j < entrys; ++j )
		{
			frame f;
			f.image	= uncompressed + reader< xgc_uint32 >( read_cursor, &read_cursor );

			std::vector< frame >::iterator it = std::find( sprites.begin(), sprites.end(), f );
			if( it == sprites.end() )
			{
				f.size		= reader< xgc_uint32 >( f.image, &f.image );
				f.format	= reader< xgc_uint32 >( f.image, &f.image );
				f.full_W	= reader< xgc_uint32 >( f.image, &f.image );
				f.full_H	= reader< xgc_uint32 >( f.image, &f.image );
				f.width		= reader< xgc_uint32 >( f.image, &f.image );
				f.height	= reader< xgc_uint32 >( f.image, &f.image );
				f.left_margin = reader< xgc_uint32 >( f.image, &f.image );
				f.top_margin = reader< xgc_uint32 >( f.image, &f.image );

				width_max = __max(width_max,f.width);
				height_max = __max(height_max,f.height);

				sprites.push_back(f);
				index_seq.push_back(sprites.size()-1);
			}
			else
			{
				// ASSERT_MSG( false, _T("has same offset") );
				index_seq.push_back(std::distance(sprites.begin(), it));
			}
		}
	}

	Animation.first = hge->Texture_Create( width_max * sprites.size(), height_max );
	xgc_int32 index = 0;
	for( std::vector< frame >::iterator i = sprites.begin(); i != sprites.end(); ++i, ++index )
	{
		const frame& f = *i;
		const xgc_byte *image = i->image;

		xgc_uint32* tex = (xgc_uint32*)hge->Texture_Lock( Animation.first, false, width_max * index, 0, f.width, f.height );

		switch( f.format )
		{
		case 0:
			// OutputDebugStringA("fromat 0\r\n");
			for( xgc_uint32 n = 0; n < f.height*f.width; ++n )
			{
				const xgc_byte* color = (const xgc_byte*)&palette[image[n]];
				tex[n] = ARGB( 255, color[0], color[1], color[2] );
			}
			break;
		case 1:
			{
				// OutputDebugStringA("fromat 1\r\n");
				const xgc_byte *base = image;	 // base of data start
				const xgc_uint32 *offset = (const xgc_uint32*)image; // offset array

				image += sizeof(xgc_uint32) * f.height;
				for( xgc_uint32 h = 0; h < f.height; ++h )
				{
					const xgc_byte* current = base + offset[h];

					xgc_uint32 row_length = 0;
					while( row_length < f.width )
					{
						xgc_byte type = *current++;
						xgc_uint32 len = *current++ + 1;

						if( type == 0xff )
						{
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								const xgc_byte* color = (const xgc_byte*)(palette + *current);
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
							current += len;
						}
						else
						{
							const xgc_byte* color = (const xgc_byte*)(palette + type);
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
						}

						row_length += len;
					}
				}
			}
			break;
		case 2:
			// OutputDebugStringA("fromat 2\r\n");
			{
				const xgc_byte* current = image + reader< xgc_uint16 >( image );

				for( xgc_uint32 h = 0; h < f.height; ++h )
				{
					xgc_uint32 row_length = 0;
					while( row_length < f.width )
					{
						xgc_byte type = reader< xgc_byte >( image, &image );
						xgc_byte code = type>>5;
						xgc_byte len = type&0x1F;

						if( code == 0x07 )
						{
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								const xgc_byte* color = (const xgc_byte*)(palette + *current);
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
							current += len;
						}
						else
						{
							const xgc_byte* color = (const xgc_byte*)(palette + code);
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
						}
						row_length += len;
					}
				}
			}
			break;
		case 3:
			{
				for( xgc_uint32 h = 0; h < f.height; ++h )
				{
					const xgc_byte* current = image + reader< xgc_uint16 >( image + h*2*(f.width/32) );

					xgc_uint32 row_length = 0;
					while( row_length < f.width )
					{
						xgc_byte type = reader< xgc_byte >( current, &current );
						xgc_byte code = type>>5;
						xgc_byte len = (type&0x1F) + 1;

						if( code == 0x07 )
						{
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								const xgc_byte* color = (const xgc_byte*)(palette + *current);
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
							current += len;
						}
						else
						{
							const xgc_byte* color = (const xgc_byte*)(palette + code);
							for( xgc_uint32 w = 0; w < len; ++w )
							{
								tex[h*f.width + w] = ARGB( 255, color[0], color[1], color[2] );
							}
						}
						row_length += len;
					}
				}
			}
			//OutputDebugStringA("fromat 3\r\n");
			break;
		}
		hge->Texture_Unlock( Animation.first );
	}

	hge->Release();
	return xgc_nullptr;
}

xgc_char* CResourceMng::CreateText( const LodEntry& entry, xgc_byte* uncompressed )
{
	return xgc_nullptr;
}

//////////////////////////////////////////////////////////////////////////
hgeSprite* CResourceMng::GetSprite( xgc_lpcstr filename )
{
	SpriteMap::iterator i = smap.find( filename );
	if( i != smap.end() )
	{
		return &i->second;
	}

	return xgc_nullptr;
}

HTEXTURE CResourceMng::GetAnimation( xgc_lpcstr filename )
{
	AnimationMap::iterator i = amap.find( filename );
	if( i != amap.end() )
	{
		return i->second.first;
	}

	return xgc_nullptr;
}
